#pragma once

#include <iostream>
#include <sys/resource.h>
#include "../comm/util.hpp"
#include "../comm/logger.hpp"
#include <fcntl.h>
#include <sys/wait.h>

namespace build{
    class Runner{
    public:
      static void setProcLimit(int _cpu_limit, int _mem_limit){
        struct rlimit cpu_limit;//这个是对时间进行限制
        cpu_limit.rlim_cur = _cpu_limit;
        cpu_limit.rlim_max = RLIM_INFINITY;
        setrlimit(RLIMIT_CPU,&cpu_limit);

        struct rlimit mem_limit;//这个是对内存进行限制
        mem_limit.rlim_cur = 1024 * _mem_limit;
        mem_limit.rlim_max = RLIM_INFINITY;
        setrlimit(RLIMIT_MEMLOCK,&mem_limit);
      }
      //负责运行可执行程序，大致逻辑和编译可执行程序一样
      static int runExe(const std::string &filename, int cpu_limit, int mem_limit){
            /*********************************************
             * 程序运行：
             * 1. 代码跑完，结果正确
             * 2. 代码跑完，结果不正确
             * 3. 代码没跑完，异常了
             * Run需要考虑代码跑完，结果正确与否吗？？不考虑！
             * 结果正确与否：是由我们的测试用例决定的！
             * 我们只考虑：是否正确运行完毕
             *
             * 我们必须知道可执行程序是谁？
             * 一个程序在默认启动的时候
             * 标准输入: 不处理
             * 标准输出: 程序运行完成，输出结果是什么
             * 标准错误: 运行时错误信息
             * *******************************************/
            int status=0;
            std::string exe = helper::PathFile::getTatget(filename);
            //标准输出
            int stdout_fd = open(helper::PathFile::getOut(filename).c_str(),O_CREAT|O_WRONLY,0644);
            //标准输入
            int stdin_fd = open(helper::PathFile::getIn(filename).c_str(),O_CREAT|O_RDONLY,0644);
            //标准错误
            int stderr_fd = open(helper::PathFile::getError(filename).c_str(),O_CREAT|O_WRONLY,0644);
            if(stdin_fd < 0 || stdout_fd < 0 || stderr_fd < 0){
                ELOG("尝试运行可执行程序[%s]时,打开文件失败！",filename);
                return -1; //代表打开文件失败
            }            
            int pid = fork();
            if( pid < 0 ){
                close(stdout_fd);
                close(stdin_fd);
                close(stderr_fd);
                ELOG("尝试运行可执行程序[%s]时，子进程创建失败!",filename);
                return -2;//创建子进程失败
            }
            else if(pid == 0){
                //子进程
                //1.开始进行重定向
                //输出重定向
                dup2(stdout_fd,0);
                //输入重定向
                dup2(stdin_fd,1);
                //标准错误重定向
                dup2(stderr_fd,2);
                //进行资源限制
                setProcLimit(cpu_limit, mem_limit);
                //开始执行
                execlp(exe.c_str(),exe.c_str(),nullptr);
                exit(1);
            }
            else{
                waitpid(pid,&status,0);
                close(stdout_fd);
                close(stdin_fd);
                close(stderr_fd);
                ILOG("运行完毕!");
                return status& 0x7F;
            }

      }
    };
}